/* main.c - Application main entry point */

/*
 * Copyright (c) 2022 Xiaomi Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <sys/printk.h>
#include <sys/reboot.h>
#include <sys/byteorder.h>
#include <zephyr.h>

#include <settings/settings.h>
#include <fs/nvs.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/mesh.h>
#include <bluetooth/gatt.h>

#include <logging/log.h>
LOG_MODULE_REGISTER(ble);

#include "mible_api.h"
#include "custom_mi_config.h"

#include <../../subsys/bluetooth/host/adv.h>
#include <../../subsys/bluetooth/host/hci_core.h>

#include <../../subsys/bluetooth/mesh/adv.h>

/**< The UUID of the Xiaomi Service. */
#define BLE_UUID_MI_SERVICE	0xFE95
/**< The UUID of the System info Characteristic. */
#define BLE_UUID_MI_SYS_INFO	0x001C
/**< The UUID of the Mi Service Version Characteristic. */
#define BLE_UUID_MI_VERS	0x0004
/**< The UUID of the WiFi Status Characteristic. */
#define BLE_UUID_MI_WIFI_CFG	0x0005
/**< The UUID of the Control Point Characteristic. */
#define BLE_UUID_MI_CTRLP	0x0010
/**< The UUID of the Secure Auth Characteristic. */
#define BLE_UUID_MI_SECURE	0x0016
/**< The UUID of the OTA Control Point Characteristic. */
#define BLE_UUID_MI_OTA_CTRL	0x0017
/**< The UUID of the OTA Data Characteristic. */
#define BLE_UUID_MI_OTA_DATA	0x0018
/**< The UUID of the Standard Auth Characteristic. */
#define BLE_UUID_MI_STANDARD	0x0019
/**< The UUID of the MIOT Spec RX Characteristic. */
#define BLE_UUID_MI_SPEC_RD	0x001A
/**< The UUID of the MIOT Spec TX Characteristic. */
#define BLE_UUID_MI_SPEC_WR	0x001B

static struct bt_uuid_16 mi_service	= BT_UUID_INIT_16(BLE_UUID_MI_SERVICE);
static struct bt_uuid_16 mi_vers	= BT_UUID_INIT_16(BLE_UUID_MI_VERS);
static struct bt_uuid_16 mi_wifi_cfg	= BT_UUID_INIT_16(BLE_UUID_MI_WIFI_CFG);
static struct bt_uuid_16 mi_ctrlp	= BT_UUID_INIT_16(BLE_UUID_MI_CTRLP);
static struct bt_uuid_16 mi_secure	= BT_UUID_INIT_16(BLE_UUID_MI_SECURE);
static struct bt_uuid_16 mi_ota_ctrl	= BT_UUID_INIT_16(BLE_UUID_MI_OTA_CTRL);
static struct bt_uuid_16 mi_ota_data	= BT_UUID_INIT_16(BLE_UUID_MI_OTA_DATA);
static struct bt_uuid_16 mi_standard	= BT_UUID_INIT_16(BLE_UUID_MI_STANDARD);
static struct bt_uuid_16 mi_spec_rd	= BT_UUID_INIT_16(BLE_UUID_MI_SPEC_RD);
static struct bt_uuid_16 mi_spec_wr	= BT_UUID_INIT_16(BLE_UUID_MI_SPEC_WR);
static struct bt_uuid_16 mi_sys_info	= BT_UUID_INIT_16(BLE_UUID_MI_SYS_INFO);

struct mible_char_val {
	uint8_t max_len;
	uint8_t data_len;
	uint8_t *data;
};

static uint8_t mi_vers_data[20];
static struct mible_char_val mi_vers_char = {
	.max_len = ARRAY_SIZE(mi_vers_data),
	.data = mi_vers_data,
};

static uint8_t mi_ctrlp_data[4];
static struct mible_char_val mi_ctrlp_char = {
	.max_len = ARRAY_SIZE(mi_ctrlp_data),
	.data = mi_ctrlp_data,
};

static struct bt_conn *g_conn;

static ssize_t mi_vers_read_cb(struct bt_conn *conn,
			       const struct bt_gatt_attr *attr, void *buf,
			       uint16_t len, uint16_t offset)
{
	return bt_gatt_attr_read(conn, attr, buf, len, offset, mi_vers_char.data,
				 mi_vers_char.data_len);
}

static ssize_t _mi_write_cb(struct bt_conn *conn,
			    const struct bt_gatt_attr *attr, const void *buf,
			    uint16_t len, mible_gatts_evt_t evt)
{
	mible_gatts_evt_param_t param;

	param.conn_handle = 0;
	param.write.value_handle = bt_gatt_attr_get_handle(attr);
	param.write.offset = 0;
	param.write.data = (void *)buf;
	param.write.len = len;

	mible_gatts_event_callback(evt, &param);

	return len;
}

static ssize_t mi_write_cb(struct bt_conn *conn,
			   const struct bt_gatt_attr *attr, const void *buf,
			   uint16_t len, uint16_t offset, uint8_t flags)
{
	return _mi_write_cb(conn, attr, buf, len, MIBLE_GATTS_EVT_WRITE);
}

static ssize_t mi_ctrlp_write_cb(struct bt_conn *conn,
				 const struct bt_gatt_attr *attr,
				 const void *buf, uint16_t len,
				 uint16_t offset, uint8_t flags)
{
	if (len > mi_ctrlp_char.max_len) {
		LOG_ERR("Too big data!");
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
	}

	memcpy(mi_ctrlp_char.data, buf, len);
	mi_ctrlp_char.data_len = len;

	return mi_write_cb(conn, attr, buf, len, offset, flags);
}

static ssize_t mi_ota_write_cb(struct bt_conn *conn,
			       const struct bt_gatt_attr *attr,
			       const void *buf, uint16_t len,
			       uint16_t offset, uint8_t flags)
{
	return _mi_write_cb(conn, attr, buf, len, MIBLE_GATTS_EVT_WRITE_PERMIT_REQ);
}

/* Vendor Primary Service Declaration */
BT_GATT_SERVICE_DEFINE(mible_lib_svc,
	BT_GATT_PRIMARY_SERVICE(&mi_service),
	BT_GATT_CHARACTERISTIC(&mi_vers.uuid,
			       BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
			       mi_vers_read_cb, NULL, &mi_vers_char),
	BT_GATT_CHARACTERISTIC(&mi_ctrlp.uuid,
			       BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_ctrlp_write_cb, &mi_ctrlp_char),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_standard.uuid,
			       BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_secure.uuid,
			       BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_wifi_cfg.uuid,
			       BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_READ,
			       NULL, mi_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_ota_ctrl.uuid,
			       BT_GATT_CHRC_WRITE | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_ota_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_ota_data.uuid,
			       BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_ota_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_spec_rd.uuid,
			       BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_spec_wr.uuid,
			       BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
	BT_GATT_CHARACTERISTIC(&mi_sys_info.uuid,
			       BT_GATT_CHRC_WRITE_WITHOUT_RESP | BT_GATT_CHRC_NOTIFY,
			       BT_GATT_PERM_WRITE,
			       NULL, mi_write_cb, NULL),
	BT_GATT_CCC(NULL, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);

struct bt_uuid *uuid_get(struct bt_uuid_16 *u1, struct bt_uuid_128 *u2, mible_uuid_t *u3)
{
	if (!u3->type) {
		u1->uuid.type = BT_UUID_TYPE_16;
		u1->val = u3->uuid16;
		return &u1->uuid;
	}

	u2->uuid.type = BT_UUID_TYPE_128;
	(void)memcpy(u2->val, u3->uuid128, BT_UUID_SIZE_128);

	return &u2->uuid;
}

struct attr_match {
	const struct bt_gatt_attr *attr;
	struct bt_uuid *uuid;
};

static uint8_t bt_gatt_attr_func(const struct bt_gatt_attr *attr,
				 uint16_t handle, void *user_data)
{
	struct attr_match *match = user_data;

	if (!attr->user_data) {
		return BT_GATT_ITER_CONTINUE;
	}

	if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_PRIMARY)) {
		return BT_GATT_ITER_CONTINUE;
	}

	if (bt_uuid_cmp(match->uuid, attr->user_data)) {
		return BT_GATT_ITER_CONTINUE;
	}

	match->attr = attr;

	return BT_GATT_ITER_STOP;
}

const char *bt_uuid_str_real(const struct bt_uuid *uuid);

mible_status_t mible_gatts_service_init(mible_gatts_db_t *p_server_db)
{
	struct bt_uuid_16 u1;
	struct bt_uuid_128 u2;
	struct bt_uuid *uuid;
	struct attr_match match = { NULL };
	struct bt_gatt_attr *attr;
	mible_arch_evt_param_t evt_param;
	mible_gatts_srv_db_t *p_mi_srv = p_server_db->p_srv_db;

	match.uuid = uuid_get(&u1, &u2, &p_mi_srv->srv_uuid);

	bt_gatt_foreach_attr(0x0001, 0xffff, bt_gatt_attr_func, &match);
	if (!match.attr) {
		LOG_ERR("Service UUID %s not found", bt_uuid_str_real(match.uuid));
		return MI_ERR_NOT_FOUND;
	}

	p_mi_srv->srv_handle = bt_gatt_attr_get_handle(match.attr);

	for (int index = 0; index < p_mi_srv->char_num; index++) {
		mible_gatts_char_db_t *cha = &p_mi_srv->p_char_db[index];

		uuid = uuid_get(&u1, &u2, &cha->char_uuid);

		attr = bt_gatt_find_by_uuid(match.attr, 0, uuid);
		if (!attr) {
			LOG_ERR("Characteristic UUID %s not found", bt_uuid_str_real(uuid));
			return MI_ERR_NOT_FOUND;
		}

		cha->char_value_handle = bt_gatt_attr_get_handle(attr);
	}

	evt_param.srv_init_cmp.p_gatts_db = p_server_db;
	evt_param.srv_init_cmp.status = MI_SUCCESS;

	mible_arch_event_callback(MIBLE_ARCH_EVT_GATTS_SRV_INIT_CMP, &evt_param);

	return MI_SUCCESS;
}

static uint8_t cb_found_attr(const struct bt_gatt_attr *attr,
			     uint16_t handle, void *user_data)
{
	const struct bt_gatt_attr **found = user_data;

	*found = attr;

	return BT_GATT_ITER_STOP;
}

static const struct bt_gatt_attr *find_attr_by_handle(uint16_t handle)
{
	const struct bt_gatt_attr *attr = NULL;

	bt_gatt_foreach_attr(handle, handle, cb_found_attr, &attr);

	return attr;
}

mible_status_t mible_gatts_value_set(uint16_t srv_handle, uint16_t value_handle,
				     uint8_t offset, uint8_t *p_value, uint8_t len)
{
	struct mible_char_val *val;
	const struct bt_gatt_attr *attr = find_attr_by_handle(value_handle);

	if (!attr || !attr->user_data) {
		LOG_ERR("Attribute handle 0x%04x not found", value_handle);
		return MI_ERR_NOT_FOUND;
	}

	val = attr->user_data;
	if (val->max_len < len) {
		LOG_ERR("Unable set attr 0x%04x value (%d > %d)",
			value_handle, len, val->max_len);

		return MI_ERR_DATA_SIZE;
	}

	(void)memcpy(val->data, p_value, len);
	val->data_len = len;

	return MI_SUCCESS;
}

mible_status_t mible_gatts_value_get(uint16_t srv_handle, uint16_t value_handle,
				     uint8_t *p_value, uint8_t *p_len)
{
	struct mible_char_val *val;
	const struct bt_gatt_attr *attr = find_attr_by_handle(value_handle);

	if (!attr || !attr->user_data) {
		LOG_ERR("Attribute handle 0x%04x not found", value_handle);
		return MI_ERR_NOT_FOUND;
	}

	val = attr->user_data;

	(void)memcpy(p_value, val->data, val->data_len);
	*p_len = val->data_len;

	return MI_SUCCESS;
}

mible_status_t mible_gatts_notify_or_indicate(uint16_t conn_handle, uint16_t srv_handle,
					      uint16_t char_value_handle, uint8_t offset,
					      uint8_t *p_value, uint8_t len, uint8_t type)
{
	int ret = 0;
	const struct bt_gatt_attr *pattr = find_attr_by_handle(char_value_handle);

	if (!pattr) {
		LOG_ERR("Attribute handle 0x%04x not found", char_value_handle);
		return MIBLE_ERR_ATT_INVALID_ATT_HANDLE;
	}

	struct bt_gatt_notify_params notify_params = {
		.attr = pattr,
		.data = p_value,
		.len = len,
	};

	if (!g_conn) {
		LOG_ERR("Not connected");
		return MI_ERR_INVALID_STATE;
	}

	ret = bt_gatt_notify_cb(g_conn, &notify_params);
	if (ret) {
		LOG_ERR("Unable send notify (err %d)", ret);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

K_MEM_SLAB_DEFINE(mible_tasks, sizeof(struct mible_task_item),
		  TASK_MAX_NUM, __alignof__(struct mible_task_item));

static K_FIFO_DEFINE(task_post);

struct mible_timer_item {
	struct k_work_delayable work;
	mible_timer_handler handler;
	void *user_data;
	uint32_t timeout_value:31,
		 mode:1;
};

K_MEM_SLAB_DEFINE(mible_timers, sizeof(struct mible_timer_item),
		  TIMER_MAX_NUM, __alignof__(struct mible_timer_item));

static void timeout_handler(void *arg)
{
	int err;
	struct mible_timer_item *item = arg;

	if (item->handler) {
		item->handler(item->user_data);
	}

	if (!item->timeout_value || !item->mode) {
		return;
	}

	err = k_work_reschedule(&item->work, K_MSEC(item->timeout_value));
	if (err < 0) {
		LOG_ERR("Reschedule work failed (err %d)", err);
	}
}

void mible_tasks_exec(void)
{
	struct mible_task_item *item = k_fifo_get(&task_post, K_FOREVER);
	mible_handler_t handler = item->handler;
	void *arg = item->arg;

	if (item->flag) {
		item->used = false;
	} else {
		k_mem_slab_free(&mible_tasks, (void **)&item);
	}

	handler(arg);
}

void mible_single_task_post(struct mible_task_item *item,
			    mible_handler_t handler, void *arg)
{
	if (item->used) {
		return;
	}

	item->node.next = NULL;
	item->handler = handler;
	item->arg = arg;
	item->flag = true;
	item->used = true;

	k_fifo_put(&task_post, item);
}

mible_status_t mible_task_post(mible_handler_t handler, void *arg)
{
	int err;
	struct mible_task_item *item = NULL;

	err = k_mem_slab_alloc(&mible_tasks, (void **)&item, K_NO_WAIT);
	if (err) {
		LOG_ERR("Unable allocation memory for task post (err %d)", err);
		return MI_ERR_RESOURCES;
	}

	item->node.next = NULL;
	item->handler = handler;
	item->arg = arg;
	item->flag = false;

	k_fifo_put(&task_post, item);

	return MI_SUCCESS;
}

static void delay_work_handler(struct k_work *work)
{
	struct k_work_delayable *pwork = CONTAINER_OF(work, struct k_work_delayable, work);
	struct mible_timer_item *item = CONTAINER_OF(pwork, struct mible_timer_item, work);
	mible_status_t status;
	int err;

	/* if unable task post due to limit number, reschedule later again.  */
	status = mible_task_post(timeout_handler, item);
	if (!status || !item->timeout_value) {
		return;
	}

	err = k_work_reschedule(&item->work, K_MSEC(item->timeout_value));
	if (err < 0) {
		LOG_ERR("Reschedule work failed (err %d)", err);
	}
}

mible_status_t mible_timer_create(void **p_timer_id, mible_timer_handler timeout_handler,
				  mible_timer_mode mode)
{
	int err;
	struct mible_timer_item *item = NULL;

	err = k_mem_slab_alloc(&mible_timers, (void **)&item, K_NO_WAIT);
	if (err) {
		LOG_ERR("Unable allocation memory for timer");
		return MI_ERR_RESOURCES;
	}

	*p_timer_id = (void *)item;
	item->mode = (mode == MIBLE_TIMER_REPEATED);
	item->handler = timeout_handler;

	k_work_init_delayable(&item->work, delay_work_handler);

	return MI_SUCCESS;
}

mible_status_t mible_user_timer_create(void **p_timer_id,
                  mible_timer_handler timeout_handler, mible_timer_mode mode)
{
	return mible_timer_create(p_timer_id, timeout_handler, mode);
}

mible_status_t mible_timer_start(void *timer_id, uint32_t timeout_value, void *p_context)
{
	int err;
	struct mible_timer_item *item = timer_id;

	if (timeout_value < 10 && item->mode == MIBLE_TIMER_REPEATED) {
		return -EINVAL;
	}

	item->timeout_value = timeout_value;
	item->user_data = p_context;

	err = k_work_reschedule(&item->work, K_MSEC(timeout_value));
	if (err < 0) {
		LOG_ERR("mible timer start failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_timer_stop(void *timer_id)
{
	int err;
	struct mible_timer_item *item = timer_id;

	if (!item->handler) {
		return MI_SUCCESS;
	}

	item->timeout_value = 0;

	err = k_work_cancel_delayable(&item->work);
	if (err) {
		LOG_ERR("mible timer stop failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_timer_delete(void *timer_id)
{
	mible_status_t status;
	struct mible_timer_item *item = timer_id;

	status = mible_timer_stop(timer_id);
	if (status != MI_SUCCESS) {
		LOG_ERR("mible timer delete failed (status %d)", status);
		return MI_ERR_INVALID_PARAM;
	}

	item->handler = NULL;
	item->mode = 0;
	item->user_data = NULL;

	k_mem_slab_free(&mible_timers, (void **)&item);

	return MI_SUCCESS;
}

static void conn_process(void *arg)
{
	mible_gap_evt_param_t param = { 0 };
	struct bt_conn_info info;
	int error;

	if (arg) {
		uint8_t reason = *(uint8_t *)&arg;

		if (reason == BT_HCI_ERR_CONN_TIMEOUT) {
			param.disconnect.reason = CONNECTION_TIMEOUT;
		} else if (reason == BT_HCI_ERR_REMOTE_USER_TERM_CONN) {
			param.disconnect.reason = REMOTE_USER_TERMINATED;
		} else {
			param.disconnect.reason = LOCAL_HOST_TERMINATED;
		}

		mible_gap_event_callback(MIBLE_GAP_EVT_DISCONNECT, &param);
		return;
	}

	error = bt_conn_get_info(g_conn, &info);

	if (info.role == BT_CONN_ROLE_CENTRAL) {
		param.connect.role = MIBLE_GAP_CENTRAL;
	} else {
		param.connect.role = MIBLE_GAP_PERIPHERAL;
	}

	if (info.le.dst->type == BT_ADDR_LE_PUBLIC) {
		param.connect.type = MIBLE_ADDRESS_TYPE_PUBLIC;
	} else {
		param.connect.type = MIBLE_ADDRESS_TYPE_RANDOM;
	}

	memcpy(param.connect.peer_addr, info.le.dst->a.val, sizeof(bt_addr_t));

	param.connect.conn_param.conn_sup_timeout = info.le.timeout;
	param.connect.conn_param.max_conn_interval = info.le.interval;
	param.connect.conn_param.min_conn_interval = info.le.interval;
	param.connect.conn_param.slave_latency = info.le.latency;

	mible_gap_event_callback(MIBLE_GAP_EVT_CONNECTED, &param);
}

static void connected(struct bt_conn *conn, uint8_t err)
{
	if (err) {
		LOG_ERR("Connection failed (err %d)", err);
		return;
	}

	g_conn = bt_conn_ref(conn);

	MIBLE_TASK_POST_SINGLE(conn_process, NULL);
}

static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	void *p = NULL;

	*(uint8_t *)&p = reason;

	bt_conn_unref(g_conn);
	g_conn = NULL;

	MIBLE_TASK_POST_SINGLE(conn_process, p);
}

BT_CONN_CB_DEFINE(mible_api) = {
	.connected = connected,
	.disconnected = disconnected,
};

mible_status_t mible_gap_disconnect(uint16_t conn_handle)
{
	int err;

	if (!g_conn) {
		LOG_ERR("Already disconnected");
		return MIBLE_ERR_INVALID_CONN_HANDLE;
	}

	err = bt_conn_disconnect(g_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
	if (err) {
		LOG_ERR("Connection termination failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_gap_update_conn_params(uint16_t conn_handle,
					    mible_gap_conn_param_t conn_params)
{
	int err;
	static struct bt_le_conn_param param;

	if (!g_conn) {
		LOG_ERR("Not connection");
		return MIBLE_ERR_INVALID_CONN_HANDLE;
	}

	param.interval_max = conn_params.max_conn_interval;
	param.interval_min = conn_params.min_conn_interval;
	param.latency = conn_params.slave_latency;
	param.timeout = conn_params.conn_sup_timeout;

	err = bt_conn_le_param_update(g_conn, &param);
	if (err) {
		LOG_ERR("Connection param update failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_gap_address_get(mible_addr_t mac)
{
	bt_addr_le_t paddr;
	size_t num = 1;

	bt_id_get(&paddr, &num);

	if (!num) {
		LOG_ERR("Bluetooth address not found");
		return MI_ERR_NOT_FOUND;
	}

	memcpy(mac, (void *)&(paddr.a), sizeof(bt_addr_t));

	return MI_SUCCESS;
}

#if defined(CONFIG_BT_MESH)
mible_status_t mible_gap_scan_start(mible_gap_scan_type_t scan_type,
				    mible_gap_scan_param_t scan_param)
{
	int err;

	err = bt_mesh_scan_enable();
	if (err) {
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_gap_scan_stop(void)
{
	int err;

	err = bt_mesh_scan_disable();
	if (err) {
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_gap_scan_param_get(mible_gap_scan_param_t *scan_param)
{
	scan_param->scan_interval = BT_MESH_SCAN_INTERVAL_MS;
	scan_param->scan_window = BT_MESH_SCAN_WINDOW_MS;

	return MI_SUCCESS;
}
#endif /* CONFIG_BT_MESH */

NET_BUF_SIMPLE_DEFINE_STATIC(adv, 31);
NET_BUF_SIMPLE_DEFINE_STATIC(srp, 31);

static mible_status_t adv_decode(struct net_buf_simple *in,
				 struct bt_data *out, uint8_t *count)
{
	uint8_t c = 0;
	struct net_buf_simple_state states;
	mible_status_t status = MI_SUCCESS;

	net_buf_simple_save(in, &states);

	while(in->len > 2) {
		if (c > *count) {
			status = MI_ERR_NO_MEM;
			LOG_ERR("Insufficient buffer provide (%d > %d)",
				c, *count);
			goto end;
		}

		out[c].data_len = net_buf_simple_pull_u8(in) - 1;
		out[c].type = net_buf_simple_pull_u8(in);
		if (out[c].data_len > in->len) {
			status = MI_ERR_DATA_SIZE;
			LOG_ERR("Invalid LTV data (%d > %d)",
				out[c].data_len, in->len);
			goto end;
		}

		out[c].data = net_buf_simple_pull_mem(in, out[c].data_len);

		c++;
	}

	*count = c;

end:
	net_buf_simple_restore(in, &states);

	return status;
}

mible_status_t mible_gap_adv_start(mible_gap_adv_param_t *p_param)
{
	int err;
	struct bt_data ad[5], sd[5];
	mible_status_t status = MI_SUCCESS;
	struct bt_le_adv_param param = { 0 };
	uint8_t ad_len = ARRAY_SIZE(ad), sd_len = ARRAY_SIZE(sd);

	param.interval_max = p_param->adv_interval_max;
	param.interval_min = p_param->adv_interval_min;

	if (p_param->adv_type == MIBLE_ADV_TYPE_CONNECTABLE_UNDIRECTED) {
		param.options = BT_LE_ADV_OPT_CONNECTABLE |
				BT_LE_ADV_OPT_ONE_TIME |
				BT_LE_ADV_OPT_USE_IDENTITY;
	} else if (p_param->adv_type == MIBLE_ADV_TYPE_SCANNABLE_UNDIRECTED) {
		param.options = BT_LE_ADV_OPT_SCANNABLE |
				BT_LE_ADV_OPT_USE_IDENTITY;
	} else if (p_param->adv_type == MIBLE_ADV_TYPE_NON_CONNECTABLE_UNDIRECTED) {
		param.options = BT_LE_ADV_OPT_USE_IDENTITY;
	} else {
		return MI_ERR_INVALID_PARAM;
	};

	if (p_param->ch_mask.ch_37_off == 1) {
		param.options |= BT_LE_ADV_OPT_DISABLE_CHAN_37;
	}

	if (p_param->ch_mask.ch_38_off == 1) {
		param.options |= BT_LE_ADV_OPT_DISABLE_CHAN_38;
	}

	if (p_param->ch_mask.ch_39_off == 1) {
		param.options |= BT_LE_ADV_OPT_DISABLE_CHAN_39;
	}

	status = adv_decode(&adv, ad, &ad_len);
	if (status) {
		LOG_ERR("Unable decode advertising data");
		return status;
	}

	if (p_param->adv_type == MIBLE_ADV_TYPE_NON_CONNECTABLE_UNDIRECTED) {
		err = bt_le_adv_start(&param, ad, ad_len, NULL, 0);
		if (err) {
			LOG_ERR("Unconnectable advertising start failed (err %d)", err);
			return MI_ERR_INTERNAL;
		}

		return MI_SUCCESS;
	}

	status = adv_decode(&srp, sd, &sd_len);
	if (status) {
		LOG_ERR("Unable decode scan response data");
		return status;
	}

	err = bt_le_adv_start(&param, ad, ad_len, sd, sd_len);
	if (err) {
		LOG_ERR("Connectable advertising start failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_gap_adv_data_set(uint8_t const *p_data, uint8_t dlen,
				      uint8_t const *p_sr_data, uint8_t srdlen)
{
	int err;
	struct bt_data ad[5], sd[5];
	mible_status_t status = MI_SUCCESS;
	uint8_t ad_len = ARRAY_SIZE(ad), sd_len = ARRAY_SIZE(sd);

	net_buf_simple_reset(&adv);
	net_buf_simple_add_mem(&adv, p_data, dlen);

	net_buf_simple_reset(&srp);

	if (p_sr_data) {
		net_buf_simple_add_mem(&srp, p_sr_data, srdlen);
	}

	status = adv_decode(&adv, ad, &ad_len);
	if (status) {
		LOG_ERR("Unable decode advertising data");
		return status;
	}

	status = adv_decode(&srp, sd, &sd_len);
	if (status) {
		LOG_ERR("Unable decode scan response data");
		return status;
	}

	if (!bt_le_adv_lookup_legacy()) {
		LOG_WRN("Advertising not enable");
		return 0;
	}

	err = bt_le_adv_update_data(ad, ad_len, sd, sd_len);
	if (err) {
		LOG_ERR("Update advertising data failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_gap_adv_stop(void)
{
	int err;

	if (!bt_le_adv_lookup_legacy()) {
		return 0;
	}

	err = bt_le_adv_stop();
	if (err) {
		LOG_ERR("Advertising stop failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

#define BT_ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5)

mible_status_t mible_gap_user_noconn_adv_start(uint32_t adv_interval, uint8_t count,
					       uint8_t const *p_data, uint8_t dlen)
{
	static struct bt_le_ext_adv *g_user_adv = NULL;

	struct bt_le_adv_param param = {
		.interval_min = BT_ADV_SCAN_UNIT(adv_interval),
		.interval_max = BT_ADV_SCAN_UNIT(adv_interval),
		.options = BT_LE_ADV_OPT_USE_IDENTITY,
	};
	struct bt_le_ext_adv_start_param start = {
		.num_events = count,
	};

	NET_BUF_SIMPLE_DEFINE(data, 31);

	struct bt_data ad[5];
	uint8_t ad_len = ARRAY_SIZE(ad);
	mible_status_t status;
	int err;

	if (!g_user_adv) {
		err = bt_le_ext_adv_create(&param, NULL,
					   &g_user_adv);
		if (err) {
			LOG_ERR("adv create failed (err %d)", err);
			return MI_ERR_INVALID_PARAM;
		}
	}

	if (atomic_test_bit(g_user_adv->flags, BT_ADV_ENABLED)) {
		return MI_ERR_BUSY;
	}

	if (net_buf_simple_tailroom(&data) < dlen) {
		return MI_ERR_INVALID_PARAM;
	}

	(void)net_buf_simple_add_mem(&data, p_data, dlen);

	status = adv_decode(&data, ad, &ad_len);
	if (status) {
		LOG_ERR("Unable decode advertising data");
		return status;
	}

	err = bt_le_ext_adv_update_param(g_user_adv, &param);
	if (err) {
		LOG_ERR("Unable set adv params");
		return MI_ERR_INVALID_PARAM;
	}

	err = bt_le_ext_adv_set_data(g_user_adv, ad, ad_len, NULL, 0);
	if (err) {
		LOG_ERR("Unable set adv data");
		return MI_ERR_INVALID_PARAM;
	}

	err = bt_le_ext_adv_start(g_user_adv, &start);
	if (err) {
		LOG_ERR("Unable start adv");
		return MI_ERR_INVALID_PARAM;
	}

	return MI_SUCCESS;
}

mible_status_t mible_rand_num_generator(uint8_t *p_buf, uint8_t len)
{
	int err;

	err = bt_rand(p_buf, len);
	if (err) {
		LOG_ERR("Random number generator failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

mible_status_t mible_aes128_encrypt(const uint8_t *key,
				    const uint8_t *plaintext, uint8_t plen,
				    uint8_t *ciphertext)
{
	int err;

	err = bt_encrypt_be(key, plaintext, ciphertext);
	if (err) {
		LOG_ERR("AES encrypt failed (err %d)", err);
		return MI_ERR_INTERNAL;
	}

	return MI_SUCCESS;
}

int mible_log_printf(const char *sFormat, ...)
{
	va_list ParamList;

	va_start(ParamList, sFormat);

	vprintk(sFormat, ParamList);

	va_end(ParamList);

	return MI_SUCCESS;
}

int mible_log_hexdump(void *array_base, uint16_t array_size)
{
	LOG_HEXDUMP_INF(array_base, array_size, "");

	return MI_SUCCESS;
}

#if defined(CONFIG_SETTINGS)
static mible_status_t record_check(void **fs, uint16_t record_id)
{
	int err;

	err = settings_storage_get(fs);
	if (err || !(*fs)) {
		LOG_ERR("Settings storage (err %d)", err);
		return MI_ERR_INVALID_PARAM;
	}

	if (record_id > 0x1000) {
		LOG_ERR("Too large record id (%d)", record_id);
		return MI_ERR_INVALID_PARAM;
	}

	return MI_SUCCESS;
}

extern struct k_mutex settings_lock;

mible_status_t mible_record_delete(uint16_t record_id)
{
	mible_status_t status = MI_SUCCESS;
	mible_arch_evt_param_t evt_param;
	struct nvs_fs *cf_nvs = NULL;
	int err;

	status = record_check((void **)&cf_nvs, record_id);
	if (status) {
		return status;
	}

	k_mutex_lock(&settings_lock, K_FOREVER);

	err = nvs_delete(cf_nvs, record_id);
	if (err && err != -ENOENT) {
		LOG_ERR("Record 0x%04x delete failed (err %d)",
			record_id, err);

		status = MI_ERR_INVALID_PARAM;
	}

	k_mutex_unlock(&settings_lock);

	if (status) {
		return status;
	}

	evt_param.record.id = record_id;
	evt_param.record.status = MI_SUCCESS;

	mible_arch_event_callback(MIBLE_ARCH_EVT_RECORD_DELETE, &evt_param);

	return MI_SUCCESS;
}

mible_status_t mible_record_read(uint16_t record_id,
				 uint8_t *p_data, uint8_t len)
{
	mible_status_t status = MI_SUCCESS;
	struct nvs_fs *cf_nvs = NULL;
	ssize_t ret;

	status = record_check((void **)&cf_nvs, record_id);
	if (status) {
		return status;
	}

	k_mutex_lock(&settings_lock, K_FOREVER);

	ret = nvs_read(cf_nvs, record_id, p_data, len);
	if (ret != (ssize_t)len) {
		LOG_ERR("Record 0x%04x delete failed (ret %d)",
			record_id, ret);

		status = MI_ERR_INVALID_PARAM;
	}

	k_mutex_unlock(&settings_lock);

	return status;
}

mible_status_t mible_record_write(uint16_t record_id,
				  const uint8_t *p_data, uint8_t len)
{
	mible_status_t status = MI_SUCCESS;
	mible_arch_evt_param_t evt_param;
	struct nvs_fs *cf_nvs = NULL;
	ssize_t ret;

	status = record_check((void **)&cf_nvs, record_id);
	if (status) {
		return status;
	}

	k_mutex_lock(&settings_lock, K_FOREVER);

	ret = nvs_write(cf_nvs, record_id, p_data, len);
	if (ret < 0) {
		LOG_ERR("Record 0x%04x delete failed (ret %d)",
			record_id, ret);

		status = MI_ERR_INVALID_PARAM;
	}

	k_mutex_unlock(&settings_lock);

	if (status) {
		return status;
	}

	evt_param.record.id = record_id;
	evt_param.record.status = MI_SUCCESS;

	mible_arch_event_callback(MIBLE_ARCH_EVT_RECORD_WRITE, &evt_param);

	return MI_SUCCESS;
}
#endif /* CONFIG_SETTINGS */

__WEAK int default_CSPRNG(uint8_t *dest, unsigned int size)
{
	int err;

	err = bt_rand(dest, size);
	if (err) {
		return false;
	}

	return true;
}

mible_status_t mible_reboot(void)
{
	sys_reboot(0);

	return MI_SUCCESS;
}
